using System;
using System.ComponentModel;
using System.ComponentModel.Design;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;


namespace Camalon.Drawing
{
	public enum ImageAlign
	{
		None,
		TopLeft,
		TopRight,
		BottomLeft,
		BottomRight,
		Center
	}
	/// <summary>
	/// Summary description for TextureFill.
	/// </summary>
	[ToolboxItem( true )]
	[ToolboxBitmap( typeof( ShapePen ), "Components.TextureFill.bmp")]
    [Designer("Camalon.Drawing.Design.TextureFillDesigner, Camalon.Drawing.Design", typeof(IDesigner))]
    public class TextureFill : ShapeFill, IMultiBrushFill
	{
		TransparentColor	_colorScales	;
		TransparentColor	_intensity;
		TransparentColor[]	_colorMap;

		private WrapMode	_wrapMode;
		private Image		_image;
		private bool		_transform = false;
		private ImageAlign  _imageAlign = ImageAlign.None;

		TextureBrush		_brush = null; 

		
		#region Constructors

		private static Bitmap DefaultImage = SystemIcons.Error.ToBitmap();

		
		public TextureFill():base(FillType.Texture)
		{
			_colorScales = Color.Empty;
			_intensity = Color.Empty;
			_colorMap= new TransparentColor[0];
			RecreateBrush();
		}

		#endregion Constructors

		protected override void Dispose(bool disposing)
		{
			if( _image != null)
			{
				_image.Dispose();
				_image = null;
			}

			if( _brush != null) 
			{
				_brush.Dispose();
				_brush = null;
			}
			base.Dispose (disposing);
		}

		private void RecreateBrush()
		{
			Image image = _image == null ? DefaultImage:_image;

			ImageAttributes imgAtt = GetImageAttributes();
			TextureBrush brush = new TextureBrush(
				image ,
				new RectangleF(new Point(0,0), image.Size),
				_imageAttributes);


			brush.WrapMode = _wrapMode;
			_brush = brush;
		}
		
		internal Size ImageSize
		{
			get
			{
				//We need this because the SHape needs the size...
				//If image is null, we return the size of the default image
				// We do not return the DefaultImage because, it should not 
				// be added to the resource file during the design time.
				// Otherwise we our resource files would be very big...
				Image image = _image == null ? DefaultImage:_image;
				return image.Size;
			}

		}

		public Brush GetBrush()
		{
			return _brush.Clone() as TextureBrush;

		}


		#region Properties
		[Description("Alignment of the Image on the associated Shape object.")]
		[DefaultValue( ImageAlign.None )]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Style")]
		public ImageAlign ImageAlign
		{
			get	
			{ 
				return _imageAlign;
			}
			set
			{ 
				if( value == _imageAlign) return;
				_imageAlign = value;	
				InvokeInternalChanged();
			}
		}

		[Description("Indicates that fills drawn by using this TextureFill object should be transformed by the associated Shape object.")]
		[DefaultValue( false )]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Style")]
		public bool Transform
		{
			get	
			{ 
				return _transform;
			}
			set
			{ 
				if( value == _transform) return;
				_transform = value;	
				InvokeInternalChanged();
			}
		}


		[Description("An Image object that represents the image with which this TextureFill object fills shapes.")]
		[DefaultValue( null )]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Style")]
		public Image Image
		{
			get	
			{ 
				return _image;
			}
			set
			{ 
				if( value == _image) return;
				_image = value;
				
				RecreateBrush();
				InvokeInternalChanged();
			}
		}

		
		[Description("A WrapMode enumeration that specifies how fills drawn by using this TextureFill object are tiled.")]
		[DefaultValue(WrapMode.Tile)]
		[RefreshProperties(RefreshProperties.All)]
		[Category("Style")]
		public WrapMode ImageWrap
		{
			get{ return _wrapMode;}
			set
			{
				if( _wrapMode == value) return;
				_wrapMode = value;
				_brush.WrapMode = _wrapMode;
				InvokeInternalChanged();			
			}
		}


		[Description("Transparency of image with which this TextureFill object fills shapes.")]
		[DefaultValue(typeof(TransparentColor), "Empty")]
		[RefreshProperties(RefreshProperties.All)]
		[Category("Style")]
		public TransparentColor ColorScales
		{
			get
			{
				return _colorScales;
			}
			set
			{
				if( _colorScales.Equals( value)) return;
				if( _colorScales.Color == System.Drawing.Color.Transparent) value = Color.Empty;
				
				_colorScales = value;
				RefreshImageAttributes();
				InvokeInternalChanged();
			}
		}


		[Description("Transparency of image with which this TextureFill object fills shapes.")]
		[DefaultValue(typeof(TransparentColor), "Empty")]
		[RefreshProperties(RefreshProperties.All)]
		[Category("Style")]
		public TransparentColor Intensity
		{
			get
			{
				return _intensity;
			}
			set
			{
				if( _intensity.Equals( value)) return;
				if( _intensity.Color == System.Drawing.Color.Transparent) value = Color.Empty;
				
				_intensity = value;
				RefreshImageAttributes();
				InvokeInternalChanged();
			}
		}

		[DesignOnly( true)]
		protected virtual void ResetColorMap()	
		{
			_colorMap=new TransparentColor[0];
			RefreshImageAttributes();
			InvokeInternalChanged();
		}


		[DesignOnly( true)]
		protected virtual bool ShouldSerializeColorMap()	
		{
			return _colorMap.Length > 0;
		}


		[RefreshProperties(RefreshProperties.All)]
		[Description("An array of color pairs of type TransparentColor. Each color pair contains an existing color (the first value) and the color that it will be mapped to (the second value). ")]
		[DesignerSerializationVisibility( DesignerSerializationVisibility.Content)]
		[Category("Style")]
		public TransparentColor[]	ColorMap
		{
			get
			{
				if( _colorMap == null) _colorMap = new TransparentColor[0];
				return _colorMap.Clone() as TransparentColor[];
			}
			set
			{ 
				if( value == null ) value = new TransparentColor[0];
				_colorMap  = value;
				RefreshImageAttributes();
				InvokeInternalChanged();
			}
		}




		float _gamma = 0f;
		[Category("Style")]
		[TypeConverter(typeof(PercentConverter))]
		[DefaultValue( 0f)]
		public float Gamma
		{
			get
			{
				return _gamma;
			}
			set
			{
				if( value == _gamma) return;

				_gamma = value;
				RefreshImageAttributes();
				InvokeInternalChanged();
			}
		}



		#region IMultiBrushFill Members
		ShapeFill _blend		= null;

		[DefaultValue(null)]
		[Category("Style")]
		public ShapeFill Blend
		{
			get{ return _blend;}
			set
			{ 
				if( value == _blend) return;

				if( value == this) 
					throw new InvalidOperationException("Blend cannot be set with containing HatchFill object!");

				bool needEvent = true;
				IMultiBrushFill mbFill = value as IMultiBrushFill;
				if( mbFill != null && mbFill.Blend == this) needEvent = false;

				if(needEvent && _blend != null) _blend.InternalChanged -= new EventHandler(OnInternalChanged);
				_blend = value;
				if(needEvent && _blend != null) _blend.InternalChanged += new EventHandler(OnInternalChanged);

				RecreateBrush();
				InvokeInternalChanged();
			}
		}

		bool _foregroundBlend = true;
		[DefaultValue(true)]
		[Category("Style")]
		public bool ForegroundBlend
		{
			get
			{
				return _foregroundBlend;
			}
			set
			{
				if( value == _foregroundBlend) return;

				_foregroundBlend = value;
				InvokeInternalChanged();
			}
		}

		#endregion


		#endregion Properties

		#region GetImageAttributes methods

		private ImageAttributes _imageAttributes = new ImageAttributes();
		private void RefreshImageAttributes()
		{
			_imageAttributes = GetImageAttributes();
			RecreateBrush();
		}
		private ImageAttributes GetImageAttributes(/* TransparentColor imageBlend,TransparentColor transparentKey */)
		{
			ImageAttributes imageAtt = new ImageAttributes();



			TransparentColor imageBlend		= _colorScales;//Scale color
			TransparentColor intensity		= _intensity;


			float A = 1f;
			float R = 1f;
			float G = 1f;
			float B = 1f;

			float Ai = 0f;
			float Ri = 0f;
			float Gi = 0f;
			float Bi = 0f;


			bool needColorMatrix = false;
			
			if( imageBlend.IsVisible )
			{	
				Color c = imageBlend;

				A = 1f-imageBlend.Transparency;
				R = ((float)c.R / 255.0f);
				G = ((float)c.G / 255.0f);
				B = ((float)c.B / 255.0f);

				needColorMatrix = true;
			}

			if( intensity.IsVisible )
			{	
				Color c = intensity;

				//Ai = 1f-imageBlend.Transparency;
				Ri = ((float)c.R / 255.0f);
				Gi = ((float)c.G / 255.0f);
				Bi = ((float)c.B / 255.0f);

				needColorMatrix = true;
			}



			if( needColorMatrix )
			{

				float[][] matrixItems ={   new float[] {R,  0,  0,  0,  0},
										   new float[] {0,  G,  0,  0,  0},
										   new float[] {0,  0,  B,  0,  0},
										   new float[] {0,  0,  0,  A,  0}, 
										   new float[] {Ri, Gi, Bi, Ai, 1}}; 

				ColorMatrix colorMatrix = new ColorMatrix(matrixItems);


				imageAtt.SetColorMatrix(colorMatrix );


			}

			//Typical values for the gamma parameter are from 1.0 to 2.2; 
			//however, values from 0.1 to 5.0 could prove useful in some circumstances.
			if( _gamma > 0)  imageAtt.SetGamma( _gamma );

			
//			if( !needColorMatrix && _colorMap.Length == 0) return null;

			if( _colorMap.Length > 0 )
			{
				int nMaps = _colorMap.Length/2;
				nMaps+=(_colorMap.Length%2);
				ColorMap[] cms = new ColorMap[nMaps];

			
				for( int i = 0; i < nMaps; i++)
				{
					int k = 2*i;
					ColorMap cm = new ColorMap();
					cm.OldColor = _colorMap[k];

					if( (k+1) >= _colorMap.Length) cm.NewColor = Color.Transparent;
					else cm.NewColor = _colorMap[k+1];

					cms[i] = cm;
				}

				imageAtt.SetRemapTable( cms );
				
			}



			return imageAtt;
		}


		#endregion GetImageAttributes methods

	
	}	
}
